import Vue from 'vue'
import Axios from 'axios'; // 引入axios
import Global from './global'; // 引入global.js=
import routeConfig from "./route-config";
import store from "../store/index.js";

import html2Canvas from "html2canvas";
import JsPDF from "jspdf";
/*
    ajax 回调
*/
Vue.prototype.Ajax_callback = function (data, funcc = {}) {
    // console.log('data', data)
    if (data.status == 'success' || +data.status === 1) {
        //回调成功方法是否被重写，若被重写，则执行，反之执行默认的成功方法
        if (funcc.hasOwnProperty('success')) {
            let func = eval(funcc.success);
            new func(data);
        } else {
            this.default_success(data);
        }
    }
    //回调失败方法是否被重写，若被重写，则执行，反之执行默认的失败方法
    if (data.status == 'fail' || data.error === 500) {

        if (funcc.hasOwnProperty('fail')) {
            let func = eval(funcc.fail);
            new func(data);
        } else {
            this.default_fail(data);
        }
    }

    if (data.status == 'timeout') {
        if (funcc.hasOwnProperty('timeout')) {
            let func = eval(funcc.timeout);
            new func(data);
        } else {
            this.default_timeout(data);
        }
    }

}

/*
    定义success的回调和默认回调
*/
Vue.prototype.default_success = function (data) {
    this.$message.success(data.msg);
}
/*
    定义fail的回调和默认回调
*/
Vue.prototype.default_fail = function (data) {
    this.$message.error(data.msg);
}
/*
    定义timeout的回调和默认回调 timeout 和 timeout 初始化需保持一致
*/
Vue.prototype.default_timeout = function (data) {
    this.$message.error(data.msg);
}
// 调试输出
Vue.prototype._echo = function (message, type = 'log') {
    if (!this.$g.debug) return;
    if (type == 'info') {
        console.info(message);
    } else if (type == 'error') {
        console.error(message);
    } else {
        console.log(message);
    }
}

Vue.prototype.e_confirm = function (param = {}) {

    //是否可通过点击遮罩关闭 MessageBox ,默认是
    if (!param.hasOwnProperty('freecolse')) {
        param.freecolse = true;
    }
    if (!param.hasOwnProperty('txt')) {
        param.txt = '确定要进行此操作吗？';
    }
    if (!param.hasOwnProperty('yes')) {
        param.yes = (data => {
            this.$message.error('非法操作！')
        })

    }
    if (!param.hasOwnProperty('cancel')) {
        param.cancel = (data => {
            this.$message.warning('已取消操作！')
        })

    }
    this.$confirm(param.txt, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        closeOnClickModal: param.freecolse,
        type: 'warning'
    }).then(() => {
        var funcc = eval(param.yes);
        new funcc;
    }).catch(() => {
        var funcc = eval(param.cancel);
        new funcc;
    });
}
Vue.prototype.in_array = function (stringToSearch, arrayToSearch) {
    // console.log(stringToSearch, arrayToSearch);
    for (var s = 0; s < arrayToSearch.length; s++) {
        var thisEntry = arrayToSearch[s].toString();
        if (thisEntry == stringToSearch) {
            return true;
        }
    }
    return false;
};



//和PHP一样的时间戳格式化函数
//@param  {string} format    格式
//@param  {int}    timestamp 要格式化的时间 默认为当前时间
//@return {string}           格式化的时间字符串
Vue.prototype.date = function (format, timestamp) {
    var a, jsdate = ((timestamp) ? new Date(timestamp * 1000) : new Date());
    var pad = function (n, c) {
        if ((n = n + "").length < c) {
            return new Array(++c - n.length).join("0") + n;
        } else {
            return n;
        }
    };
    var txt_weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
    var txt_ordin = {
        1: "st",
        2: "nd",
        3: "rd",
        21: "st",
        22: "nd",
        23: "rd",
        31: "st"
    };
    var txt_months = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
    var f = {
        // Day
        d: function () {
            return pad(f.j(), 2);
        },
        D: function () {
            t = f.l();
            return t.substr(0, 3);
        },
        j: function () {
            return jsdate.getDate();
        },
        l: function () {
            return txt_weekdays[f.w()];
        },
        N: function () {
            return f.w() + 1;
        },
        S: function () {
            return txt_ordin[f.j()] ? txt_ordin[f.j()] : 'th';
        },
        w: function () {
            return jsdate.getDay();
        },
        z: function () {
            return (jsdate - new Date(jsdate.getFullYear() + "/1/1")) / 864e5 >> 0;
        },
        // Week
        W: function () {
            var a = f.z(),
                b = 364 + f.L() - a;
            var nd2, nd = (new Date(jsdate.getFullYear() + "/1/1").getDay() || 7) - 1;
            if (b <= 2 && ((jsdate.getDay() || 7) - 1) <= 2 - b) {
                return 1;
            } else {
                if (a <= 2 && nd >= 4 && a >= (6 - nd)) {
                    nd2 = new Date(jsdate.getFullYear() - 1 + "/12/31");
                    return date("W", Math.round(nd2.getTime() / 1000));
                } else {
                    return (1 + (nd <= 3 ? ((a + nd) / 7) : (a - (7 - nd)) / 7) >> 0);
                }
            }
        },
        // Month
        F: function () {
            return txt_months[f.n()];
        },
        m: function () {
            return pad(f.n(), 2);
        },
        M: function () {
            t = f.F();
            return t.substr(0, 3);
        },
        n: function () {
            return jsdate.getMonth() + 1;
        },
        t: function () {
            var n;
            if ((n = jsdate.getMonth() + 1) == 2) {
                return 28 + f.L();
            } else {
                if (n & 1 && n < 8 || !(n & 1) && n > 7) {
                    return 31;
                } else {
                    return 30;
                }
            }
        },
        // Year
        L: function () {
            var y = f.Y();
            return (!(y & 3) && (y % 1e2 || !(y % 4e2))) ? 1 : 0;
        },
        //o not supported yet
        Y: function () {
            return jsdate.getFullYear();
        },
        y: function () {
            return (jsdate.getFullYear() + "").slice(2);
        },
        // Time
        a: function () {
            return jsdate.getHours() > 11 ? "pm" : "am";
        },
        A: function () {
            return f.a().toUpperCase();
        },
        B: function () {
            // peter paul koch:
            var off = (jsdate.getTimezoneOffset() + 60) * 60;
            var theSeconds = (jsdate.getHours() * 3600) +
                (jsdate.getMinutes() * 60) +
                jsdate.getSeconds() + off;
            var beat = Math.floor(theSeconds / 86.4);
            if (beat > 1000) beat -= 1000;
            if (beat < 0) beat += 1000;
            if ((String(beat)).length == 1) beat = "00" + beat;
            if ((String(beat)).length == 2) beat = "0" + beat;
            return beat;
        },
        g: function () {
            return jsdate.getHours() % 12 || 12;
        },
        G: function () {
            return jsdate.getHours();
        },
        h: function () {
            return pad(f.g(), 2);
        },
        H: function () {
            return pad(jsdate.getHours(), 2);
        },
        i: function () {
            return pad(jsdate.getMinutes(), 2);
        },
        s: function () {
            return pad(jsdate.getSeconds(), 2);
        },
        //u not supported yet
        // Timezone
        //e not supported yet
        //I not supported yet
        O: function () {
            var t = pad(Math.abs(jsdate.getTimezoneOffset() / 60 * 100), 4);
            if (jsdate.getTimezoneOffset() > 0) t = "-" + t;
            else t = "+" + t;
            return t;
        },
        P: function () {
            var O = f.O();
            return (O.substr(0, 3) + ":" + O.substr(3, 2));
        },
        //T not supported yet
        //Z not supported yet
        // Full Date/Time
        c: function () {
            return f.Y() + "-" + f.m() + "-" + f.d() + "T" + f.h() + ":" + f.i() + ":" + f.s() + f.P();
        },
        //r not supported yet
        U: function () {
            return Math.round(jsdate.getTime() / 1000);
        }
    };
    return format.replace(/[\\]?([a-zA-Z])/g, function (t, s) {
        if (t != s) {
            // escaped
            ret = s;
        } else if (f[s]) {
            // a date function exists
            ret = f[s]();
        } else {
            // nothing special
            ret = s;
        }
        return ret;
    });
}
/**
 * 获取access_token 方法封装
 */
Vue.prototype.get_access_token = function () {
    var arr = {
        client_id: Global.client_id,
        secret: Global.secret
    }
    var data = JSON.stringify(arr);
    Axios.get('/accessToken?company=BL&device_name=TE-0600R&data=' + data)
        .then(response => {
            let data = response.data
            Global.access_token = data.access_token;
            localStorage.setItem('access_token', Global.access_token);
        });
}
/**
 * 跳转去个人空间和名师中心模块：
 * 目前针前台首页、名师列表页 所有的点击用户头像跳转的功能
 * @pathName  {string} 模块路径，前面不需要斜杆
 * @id  {string} 附带的参数
 * @pushCode  {string} 推送的code,如果是32个0（说明没推送过），或者不传的时候（跳名师中心），在跳转的时，则要使用另外的参数app_code
 * @appCode  {string} 用户原来的平台code;
 */
Vue.prototype.goToAppModel = function (pathName, id, pushCode, appCode) {
    if (!localStorage.getItem('logintoken')) {
        this.$message.warning("先登陆个人账号，才能进行此操作哦~")
        return
    }
    const code = ["00000000000000000000000000000000", undefined].includes(pushCode) ? appCode : pushCode;
    const currentAppCode = sessionStorage.getItem('CheckSchoolAppCode');
    if (this.$store.getters.isMulti && code != currentAppCode) {
        const protocol = sessionStorage.getItem('protocol');
        const host = sessionStorage.getItem('host');
        let token = localStorage.getItem('logintoken');
        token = token ? token : '--';
        const url = `${protocol}//${host}/login?token=${token}&protocol=${protocol}&host=${host}&routerUrl=${pathName}&id=${id}&CheckSchoolAppCode=${code}`;
        window.open(url, '_blank');
    } else {
        this.$router.push({ path: `/${pathName}`, query: { id: id } }).catch(() => { });
    }
}
/**
 * 表单验证规则
 */
Vue.prototype.rules = {
    account: {
        reg: /^[\w]{4,16}$/,
        msg: '账号由4~16位英文、数字、下划线组成'
    },
    pwd: {
        reg: /^[\w]{6,16}$/,
        msg: '密码由6~16位英文、数字、下划线组成'
    },
    strongPwd: {
        reg: /^(?![\da-z]+$)(?![\dA-Z]+$)(?![\d!#$%^&*]+$)(?![a-zA-Z]+$)(?![a-z!#$%^&*]+$)(?![A-Z!#$%^&*]+$)[\da-zA-z!#$%^&*]{8,16}$/,
        msg: '密码限制8~16，由数字、大写字母、小写字母，特殊符号3种或3种以上组合组成'
    },
    spaceKey: {
        reg: /^\S+$/,
        msg: '请勿输入空格'
    },
    trim: {
        reg: /(^\S+)|(\S+$)/,
        msg: '内容前后不可有空格'
    },
    leftTrim: {
        reg: /^\S/,
        msg: '内容前后不可有空格'
    },
    rightTrim: {
        reg: /\S$/,
        msg: '内容前后不可有空格'
    },
    mobile: {
        reg: /^[1][3,4,5,6,7,8,9][0-9]{9}$|^(([0\+]\d{2,3}-)?(0\d{2,3})-)(\d{7,8})(-(\d{3,}))?$/,
        msg: '电话格式不正确'
    },
    ip: {
        reg: /^(?:(?:2[0-4][0-9]\.)|(?:25[0-5]\.)|(?:1[0-9][0-9]\.)|(?:[1-9][0-9]\.)|(?:[0-9]\.)){3}(?:(?:2[0-5][0-5])|(?:25[0-5])|(?:1[0-9][0-9])|(?:[1-9][0-9])|(?:[0-9]))$/,
        msg: '请输入正确的ip地址'
    },
    port: {
        reg: /^([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{4}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/,
        msg: '请输入正确的端口'
    },
    mask: {
        reg: /^(254|252|248|240|224|192|128|0)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)$/,
        msg: '请输入正确的子网掩码'
    },
    chinese: {
        reg: /^[\u4e00-\u9fa5\w]+$/,
        msg: '请勿输入特殊字符'
    },
    link: {
        reg: /^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i,
        msg: '链接格式错误'
    }
}

//时间格式化
Vue.prototype.formatTime1 = function (time) {
    var date = new Date(time);

    var year = date.getFullYear(),
        month = date.getMonth() + 1,//月份是从0开始的
        day = date.getDate(),
        hour = date.getHours(),
        min = date.getMinutes(),
        sec = date.getSeconds();
    var newTime = year + '-' + month + '-' + day
    return newTime;
}

//获取某个日期时间距离现在多久
Vue.prototype.getDateDiff = function (dateStr) {
    var publishTime = Date.parse(dateStr.replace(/-/gi, "/")) / 1000,
        d_seconds,
        d_minutes,
        d_hours,
        d_days,
        timeNow = parseInt(new Date().getTime() / 1000),
        d,

        date = new Date(publishTime * 1000),
        Y = date.getFullYear(),
        M = date.getMonth() + 1,
        D = date.getDate(),
        H = date.getHours(),
        m = date.getMinutes(),
        s = date.getSeconds();
    //小于10的在前面补0
    if (M < 10) {
        M = '0' + M;
    }
    if (D < 10) {
        D = '0' + D;
    }
    if (H < 10) {
        H = '0' + H;
    }
    if (m < 10) {
        m = '0' + m;
    }
    if (s < 10) {
        s = '0' + s;
    }

    d = timeNow - publishTime;
    d_days = parseInt(d / 86400);
    d_hours = parseInt(d / 3600);
    d_minutes = parseInt(d / 60);
    d_seconds = parseInt(d);

    if (d_days > 0 && d_days < 3) {
        return d_days + '天前';
    } else if (d_days <= 0 && d_hours > 0) {
        return d_hours + '小时前';
    } else if (d_hours <= 0 && d_minutes > 0) {
        return d_minutes + '分钟前';
    } else if (d_seconds < 60) {
        if (d_seconds <= 0) {
            return '刚刚';
        } else {
            return d_seconds + '秒前';
        }
    } else if (d_days >= 3 && d_days < 30) {
        return M + '-' + D + ' ' + H + ':' + m;
    } else if (d_days >= 30) {
        // return Y + '-' + M + '-' + D + ' ' + H + ':' + m;
        return M + '-' + D + ' ' + H + ':' + m;
    }
}
// 导出压缩包、二维码、xls
Vue.prototype.export_excel = function (url = '', params = {}, name = '导出数据', flag) {
    if (is_empty(url)) {
        this.$message.warning("导出地址不能为空！")
        return false;
    }

    this.$ajax.post(url, params, { responseType: 'blob' }).then(res => {
        if (!res) return;
        let filename = null;
        let blob = res.data;
        if (flag === 'zip') {
            filename = res.headers['content-disposition'] ? res.headers['content-disposition'].split(";")[1].split("filename=")[1] : name + '.zip';
        } else if (flag === 'qr') {
            filename = res.headers['content-disposition'] ? res.headers['content-disposition'].split(";")[1].split("filename=")[1] : name;
            filename = decodeURIComponent(filename);
            // filename =  res.headers['content-disposition'].split(";")[2];
        } else {
            filename = res.headers['content-disposition'] ? res.headers['content-disposition'].split(";")[1].split("filename=")[1] : name + '.xls';
        }
        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var blobURL = window.URL.createObjectURL(blob);
            var tempLink = document.createElement('a');
            tempLink.style.display = 'none';
            tempLink.href = blobURL;
            tempLink.setAttribute('download', filename);

            if (typeof tempLink.download === 'undefined') {
                tempLink.setAttribute('target', '_blank');
            }

            document.body.appendChild(tempLink);
            tempLink.click();
            document.body.removeChild(tempLink);
            window.URL.revokeObjectURL(blobURL);
        }
    })
}


/**
 * 导出信息
 */
Vue.prototype.exportExcelGet = function (url = '', params = {}, name = '导出数据', flag) {
    if (is_empty(url)) {
        this.$message.warning("导出地址不能为空！")
        return false;
    }

    this.$ajax.get(url, { params, responseType: 'blob' }).then(res => {
        if (!res) return;
        let filename = null;
        let blob = res.data;

        if (flag === 'zip') {
            filename = res.headers['content-disposition'] ? res.headers['content-disposition'].split(";")[1].split("filename=")[1] : name + '.zip';
        } else if (flag === 'qr') {
            filename = res.headers['content-disposition'] ? res.headers['content-disposition'].split(";")[1].split("filename=")[1] : name;
        } else {
            filename = res.headers['content-disposition'] ? res.headers['content-disposition'].split(";")[1].split("filename=")[1] : name + '.xls';
        }

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var blobURL = window.URL.createObjectURL(blob);
            var tempLink = document.createElement('a');
            tempLink.style.display = 'none';
            tempLink.href = blobURL;
            tempLink.setAttribute('download', filename);

            if (typeof tempLink.download === 'undefined') {
                tempLink.setAttribute('target', '_blank');
            }

            document.body.appendChild(tempLink);
            tempLink.click();
            document.body.removeChild(tempLink);
            window.URL.revokeObjectURL(blobURL);
        }
    })
}
/**
    获取网站标题
*/
Vue.prototype.getTitle = function () {
    let success = (data => {
        if (!is_empty(data.data)) {
            sessionStorage.setItem('siteTitle', data.data.name);
            sessionStorage.setItem('siteIcon', (this.BASE_URL + data.data.path));
            document.title = data.data.name;

            let link = document.querySelector("link[rel*='icon']") || document.createElement('link');
            link.type = 'image/x-icon';
            link.rel = 'shortcut icon';
            link.href = this.BASE_URL + data.data.path;
            document.getElementsByTagName('head')[0].appendChild(link);
        } else {
            sessionStorage.setItem('siteTitle', '');
        }
    });
    let fail = (data => {
        sessionStorage.setItem('siteTitle', '');
    })
    this.$ajax.get('api/setting/browser_set', { params: {}, func: { success, fail } })
}
/*
    获取网站图标 
*/
Vue.prototype.getLogo = function () {
    let success = res => {
        sessionStorage.setItem("logoUrl", res.data[0].app_logo);//应用页的标题图标
    };
    let fail = (data => {

    })
    this.$ajax({
        url: "api/system/get_logo",
        method: "get",
        params: {},
        func: { success: success }
    });
}
/*  
   排课评课权限校验
   1.未登录：排课列表，精品课程；
   2.教研员：排课列表，精品课程，排课评级；
   3.教师：排课列表，精品课程，我的预约，教学反思
*/
Vue.prototype.filtCourseAuth = function () {
    var authArr = ["list", "excellent"];
    if (this.checkTeacher()) {
        authArr.push.apply(authArr, ["reflect", "reservation"]);
    } else if (this.checkReser()) {
        authArr.push.apply(authArr, ["class"]);
    }
    return authArr;
},
    /**
     * @author lucky
     * @DateTime 2019-07-12T08:36:04+0800
     * @校验权限
     */
    Vue.prototype.checkRouter = function (token) {
        //模块下标
        let moduleData = routeConfig;
        let routerData = [];
        //权限验证
        for (let k = 0; k < moduleData.length; k++) {
            //是否登录 , 未登录跳过必须登录的模块的路由数据写入动态路由
            //2023-03-25发现这里无法正确获取到登录状态，因而暂时不采用一下逻辑
            // console.log('checkRouter', sessionStorage.getItem('user_info_str'), in_array(moduleData[k].name, Global.loginModule))
            // if (is_empty(sessionStorage.getItem('user_info_str')) && in_array(moduleData[k].name, Global.loginModule)) {
            //     continue;
            // }
            switch (moduleData[k].name) {
                case 'course':
                    let moduleRoute = [];
                    let authArr = this.filtCourseAuth();
                    let childRouteAuthName = [];
                    for (let j = 0; j < moduleData[k].children.length; j++) {
                        let ele = moduleData[k].children[j];
                        let temp_route = {};
                        if (!in_array(ele.authName, authArr)) {
                            continue;
                        }
                        temp_route.path = ele.path;
                        temp_route.name = ele.name;
                        temp_route.authName = ele.authName;
                        temp_route.txtName = ele.txtName;
                        temp_route.component = ele.component;
                        temp_route.showNav = ele.showNav;
                        moduleRoute.push(temp_route);
                        if (!is_empty(ele.childrenNode)) {
                            childRouteAuthName.push.apply(childRouteAuthName, ele.childrenNode);
                        }
                    }
                    if (!is_empty(childRouteAuthName)) {
                        let moduleRouteAuthName = getOneColumn(
                            moduleData[k].children,
                            "authName"
                        );
                        for (let i = childRouteAuthName.length - 1; i >= 0; i--) {
                            let inx = moduleRouteAuthName.indexOf(childRouteAuthName[i]);
                            if (inx > -1) {
                                moduleRoute.push(moduleData[k].children[inx]);
                            }
                        }
                    }
                    moduleData[k].children = moduleRoute;
                    routerData.push(moduleData[k]);
                    break;
                default:
                    routerData.push(moduleData[k]);
                    break;
            }
        }
        //返回动态路由
        return routerData;
    },

    //是否是老师
    Vue.prototype.checkTeacher = function () {
        if (!is_empty(store.getters.userInfo)) {
            return in_array(6, store.getters.userInfo.role) ? true : false;
        }
        return false;
    },

    //是否是教研员
    Vue.prototype.checkReser = function () {
        if (!is_empty(store.getters.userInfo)) {
            return in_array(8, store.getters.userInfo.role) ? true : false;
        }
        return false;
    }


/**
* 全局的点击跳转去公开课详情页面看视频的功能
*
* @param {Number} tranStatus 转码的状态值，已转码值等于1
* @param {Number} theId  视频的id
* @param {String} otherParam  除了id参数以外的参数，写法如：&name=大佬
*/
Vue.prototype.goToDetailPage = function (tranStatus, theId, otherParam) {
    // console.log(tranStatus, theId, otherParam)
    let self = this;
    // 判断转码情况
    function checkTranOver() {
        if (tranStatus === 1) {
            const thePath = `/home/openclassDetail?id=${theId}${otherParam ? otherParam : ''}`;
            self.$router.push({ path: thePath });
        } else {
            const tip = '该视频正在转码中，请在转码成功后点击播放，您可在后台查看视频转码进度。';
            self.$alert(tip, '提示', {
                confirmButtonText: '确定',
                callback: action => {
                    return false
                }
            });
        }
    }

    // 判断限制情况
    function checkLimit() {
        let params = {
            type: 1, // (1 视频 2 直播)
            type_id: theId, // （视频或直播id）, 
            user_id: self.$store.getters.userInfo.id // （用户id）
        }
        let success = res => {
            if (parseInt(res.data.islimit) === 1) {
                checkTranOver()
            } else {
                self.$message.error('无法查看该视频，请联系管理员开通观看权限！');
            }
        }
        self.$ajax.get('api/video/get_islimit_by_video_user_id', { params, func: { success } })
    }

    if (self.$store.getters.isMulti) {//多级版
        checkTranOver()
    } else {//单机版
        checkLimit();
    }
}



const A4_WIDTH = 592.28;
const A4_HEIGHT = 841.89;
async function toCanvas(element, width) {
    const canvas = await html2Canvas(element, {
        allowTaint: true, // 允许渲染跨域图片scale: window.devicePixelRatio * 2// 增加清晰度
    });
    // 获取canavs转化后的宽度
    const canvasWidth = canvas.width;
    // 获取canvas转化后的高度
    const canvasHeight = canvas.height;
    // 高度转化为PDF的高度
    const height = (width / canvasWidth) * canvasHeight;
    // 转化成图片Data
    const canvasData = canvas.toDataURL('image/jpeg', 1.0);
    //console.log(canvasData)
    return { width, height, data: canvasData };
}


/**
 * 生成pdf(A4多页pdf截断问题， 包括页眉、页脚 和 上下左右留空的护理)
 * @param {Object} param
 * @param {HTMLElement} param.element - 需要转换的dom根节点
 * @param {number} [param.contentWidth=550] - 一页pdf的内容宽度，0-592.28
 * @param {string} [param.filename='document.pdf'] - pdf文件名
 * @param {HTMLElement} param.header - 页眉dom元素
 * @param {HTMLElement} param.footer - 页脚dom元素
 */
Vue.prototype.htmlToPDf = async function ({ element, footer, header, contentWidth = 550, filename = "测试A4分页.pdf" }) {

    if (!(element instanceof HTMLElement)) {
        return;
    }

    // jsPDFs实例
    const pdf = new JsPDF({
        unit: 'pt',
        format: 'a4',
        orientation: 'p',
    });

    // 一页的高度， 转换宽度为一页元素的宽度
    const { width, height, data } = await toCanvas(element, contentWidth);


    // 添加页脚
    async function addHeader(header, pdf, contentWidth) {
        const { height: headerHeight, data: headerData, width: hWidth } = await toCanvas(header, contentWidth);
        pdf.addImage(headerData, 'JPEG', 0, 0, contentWidth, headerHeight);
    }

    // 添加页眉
    async function addFooter(pageNum, now, footer, pdf, contentWidth) {
        const newFooter = footer.cloneNode(true);
        console.info(newFooter)
        newFooter.querySelector('.pdf-footer-page').innerText = now;
        newFooter.querySelector('.pdf-footer-page-count').innerText = pageNum;
        document.documentElement.append(newFooter);
        const { height: footerHeight, data: footerData, width: fWidth } = await toCanvas(newFooter, contentWidth);
        pdf.addImage(footerData, 'JPEG', 0, A4_HEIGHT - footerHeight, contentWidth, footerHeight)

    }

    // 添加
    function addImage(_x, _y, pdf, data, width, height) {
        pdf.addImage(data, 'JPEG', _x, _y, width, height);
    }

    // 增加空白遮挡
    function addBlank(x, y, width, height, pdf) {
        pdf.setFillColor(255, 255, 255);
        pdf.rect(x, y, Math.ceil(width), Math.ceil(height), 'F');
    };


    // 页脚元素 经过转换后在PDF页面的高度
    const { height: tfooterHeight } = await toCanvas(footer, contentWidth)

    // 页眉元素 经过转换后在PDF的高度
    const { height: theaderHeight } = await toCanvas(header, contentWidth);

    // 距离PDF左边的距离，/ 2 表示居中 
    const baseX = (A4_WIDTH - contentWidth) / 2;
    // 预留空间给左边// 距离PDF 页眉和页脚的间距， 留白留空
    const baseY = 15;

    // 出去页头、页眉、还有内容与两者之间的间距后 每页内容的实际高度
    const originalPageHeight = (A4_HEIGHT - tfooterHeight - theaderHeight - 2 * baseY);

    // 元素在网页页面的宽度
    const elementWidth = element.offsetWidth;

    // PDF内容宽度 和 在HTML中宽度 的比， 用于将 元素在网页的高度 转化为 PDF内容内的高度， 将 元素距离网页顶部的高度  转化为 距离Canvas顶部的高度
    const rate = contentWidth / elementWidth

    // 每一页的分页坐标， PDF高度， 初始值为根元素距离顶部的距离
    const pages = [rate * getElementTop(element)];


    // 获取元素距离网页顶部的距离
    // 通过遍历offsetParant获取距离顶端元素的高度值
    function getElementTop(element) {
        let actualTop = element.offsetTop;
        let current = element.offsetParent;

        while (current && current !== null) {
            actualTop += current.offsetTop;
            current = current.offsetParent;
        }
        return actualTop;
    }


    // 遍历正常的元素节点
    function traversingNodes(nodes) {
        for (let i = 0; i < nodes.length; ++i) {
            const one = nodes[i];
            // 需要判断跨页且内部存在跨页的元素
            const isDivideInside = one.classList && one.classList.contains('divide-inside');
            // 图片元素不需要继续深入，作为深度终点
            const isIMG = one.tagName === 'IMG';
            // table的每一行元素也是深度终点
            const isTableCol = one.classList && ((one.classList.contains('ant-table-row')));
            // 特殊的富文本元素
            const isEditor = one.classList && (one.classList.contains('editor'));
            // 对需要处理分页的元素，计算是否跨界，若跨界，则直接将顶部位置作为分页位置，进行分页，且子元素不需要再进行判断
            let { offsetHeight } = one;
            // 计算出最终高度
            let offsetTop = getElementTop(one);

            // dom转换后距离顶部的高度// 转换成canvas高度
            const top = rate * (offsetTop)

            // 对于需要进行分页且内部存在需要分页（即不属于深度终点）的元素进行处理
            if (isDivideInside) {
                // 执行位置更新操作
                updatePos(rate * offsetHeight, top, one);
                // 执行深度遍历操作
                traversingNodes(one.childNodes);
            }
            // 对于深度终点元素进行处理
            else if (isTableCol || isIMG) {
                // dom高度转换成生成pdf的实际高度// 代码不考虑dom定位、边距、边框等因素，需在dom里自行考虑，如将box-sizing设置为border-boxupdatePos(rate * offsetHeight, top, one);
            }
            else if (isEditor) {
                // 执行位置更新操作
                updatePos(rate * offsetHeight, top, one);
                // 遍历富文本节点
                traversingEditor(one.childNodes)
            }
            // 对于普通元素，则判断是否高度超过分页值，并且深入
            else {
                // 执行位置更新操作
                updateNomalElPos(top)
                // 遍历子节点
                traversingNodes(one.childNodes);
            }
        }
        return;
    }




    // 对于富文本元素，观察所得段落之间都是以<p> / <img> 元素相隔，因此不需要进行深度遍历 (仅针对个人遇到的情况)
    function traversingEditor(nodes) {
        // 遍历子节点
        for (let i = 0; i < nodes.length; ++i) {
            const one = nodes[i];
            let { offsetHeight } = one;
            let offsetTop = getElementTop(one);
            const top = contentWidth / elementWidth * (offsetTop)
            updatePos(contentWidth / elementWidth * offsetHeight, top, one);
        }
    }

    // 普通元素更新位置的方法// 普通元素只需要考虑到是否到达了分页点，即当前距离顶部高度 - 上一个分页点的高度 大于 正常一页的高度，则需要载入分页点 
    function updateNomalElPos(top) {
        if (top - (pages.length > 0 ? pages[pages.length - 1] : 0) > originalPageHeight) {
            pages.push((pages.length > 0 ? pages[pages.length - 1] : 0) + originalPageHeight);
        }
    }

    // 可能跨页元素位置更新的方法// 需要考虑分页元素，则需要考虑两种情况// 1. 普通达顶情况，如上// 2. 当前距离顶部高度加上元素自身高度 大于 整页高度，则需要载入一个分页点
    function updatePos(eheight, top) {
        // 如果高度已经超过当前页，则证明可以分页了
        if (top - (pages.length > 0 ? pages[pages.length - 1] : 0) >= originalPageHeight) {
            pages.push((pages.length > 0 ? pages[pages.length - 1] : 0) + originalPageHeight);
        }
        // 若 距离当前页顶部的高度 加上元素自身的高度 大于 一页内容的高度, 则证明元素跨页，将当前高度作为分页位置
        else if ((top + eheight - (pages.length > 0 ? pages[pages.length - 1] : 0) > originalPageHeight) && (top != (pages.length > 0 ? pages[pages.length - 1] : 0))) {
            pages.push(top);
        }
    }

    // 深度遍历节点的方法
    traversingNodes(element.childNodes);
    // 可能会存在遍历到底部元素为深度节点，可能存在最后一页位置未截取到的情况
    if (pages[pages.length - 1] + originalPageHeight < height) {
        pages.push(pages[pages.length - 1] + originalPageHeight);
    }
    //console.log({ pages, contentWidth, width,height })
    // 根据分页位置 开始分页
    for (let i = 0; i < pages.length; ++i) {
        // message.success(`共${pages.length}页， 生成第${i + 1}页`)
        // 根据分页位置新增图片
        addImage(baseX, baseY + theaderHeight - pages[i], pdf, data, width, height);
        // 将 内容 与 页眉之间留空留白的部分进行遮白处理
        addBlank(0, theaderHeight, A4_WIDTH, baseY, pdf);
        // 将 内容 与 页脚之间留空留白的部分进行遮白处理
        addBlank(0, A4_HEIGHT - baseY - tfooterHeight, A4_WIDTH, baseY, pdf);
        // 对于除最后一页外，对 内容 的多余部分进行遮白处理
        if (i < pages.length - 1) {
            // 获取当前页面需要的内容部分高度
            const imageHeight = pages[i + 1] - pages[i];
            // 对多余的内容部分进行遮白
            addBlank(0, baseY + imageHeight + theaderHeight, A4_WIDTH, A4_HEIGHT - (imageHeight), pdf);
        }
        // 添加页眉
        // await addHeader(header, pdf, A4_WIDTH)
        // // 添加页脚
        // await addFooter(pages.length, i + 1, footer, pdf, A4_WIDTH);

        // 若不是最后一页，则分页
        if (i !== pages.length - 1) {
            // 增加分页
            pdf.addPage();
        }
    }
    return pdf.save(filename)
}